import inspect
from collections import namedtuple
from functools import wraps
from typing import Callable, TypeVar

from fastapi import status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse

# 定义返回类型的泛型
RT = TypeVar('RT')  

# 定义一个命名元组，用于表示HTTP响应代码和消息
HttpCode = namedtuple('HttpResp', ['code', 'message'])

class HttpResp:
    """HTTP响应结果
    """
    # 使用FastAPI提供的标准HTTP状态码
    SUCCESS = HttpCode(status.HTTP_200_OK, '成功')
    FAILED = HttpCode(status.HTTP_400_BAD_REQUEST, '失败')
    PARAMS_VALID_ERROR = HttpCode(status.HTTP_422_UNPROCESSABLE_ENTITY, '参数校验错误')
    PARAMS_TYPE_ERROR = HttpCode(status.HTTP_400_BAD_REQUEST, '参数类型错误')
    REQUEST_METHOD_ERROR = HttpCode(status.HTTP_405_METHOD_NOT_ALLOWED, '请求方法错误')
    ASSERT_ARGUMENT_ERROR = HttpCode(status.HTTP_400_BAD_REQUEST, '断言参数错误')

    LOGIN_ACCOUNT_ERROR = HttpCode(status.HTTP_401_UNAUTHORIZED, '用户名或密码错误')
    LOGIN_DISABLE_ERROR = HttpCode(status.HTTP_403_FORBIDDEN, '当前账户已被禁止登录')
    TOKEN_EMPTY = HttpCode(status.HTTP_401_UNAUTHORIZED, '未提供token或无效')
    TOKEN_INVALID = HttpCode(status.HTTP_401_UNAUTHORIZED, '无效token')

    NO_PERMISSION = HttpCode(status.HTTP_403_FORBIDDEN, '无相关权限')
    REQUEST_404_ERROR = HttpCode(status.HTTP_404_NOT_FOUND, '请求接口不存在')

    SYSTEM_ERROR = HttpCode(status.HTTP_500_INTERNAL_SERVER_ERROR, '系统错误')
    SYSTEM_TIMEOUT_ERROR = HttpCode(status.HTTP_504_GATEWAY_TIMEOUT, '请求超时')

def unified_resp(func: Callable[..., RT]) -> Callable[..., RT]:
    """
    统一响应装饰器，用于将函数的返回值封装为统一的JSON响应格式。

    :param func: 被装饰的函数
    :return: 封装后的异步函数
    """
    @wraps(func)
    async def wrapper(*args, **kwargs) -> RT:
        # 检查被装饰的函数是否为异步函数
        if inspect.iscoroutinefunction(func):
            # 如果是异步函数，则异步调用并获取结果
            resp = await func(*args, **kwargs) or []
        else:
            # 如果是同步函数，则直接调用并获取结果
            resp = func(*args, **kwargs) or []
        # 返回统一格式的JSON响应
        return JSONResponse(
            content=jsonable_encoder(
                # 正常请求响应
                {'code': HttpResp.SUCCESS.code, 'message': HttpResp.SUCCESS.message, 'data': resp},
                by_alias=False),
            media_type='application/json;charset=utf-8'
        )

    return wrapper